/**
 *  ||==\\ ==|| \\===
 *  ||  || ==||  \==/
 *  ||==// ==|| ===// (www.d3s.net)
 * 
 * KeyframeMesh is a part of the D3S Papervision3D Importer Library
 * Please feel free to use, modify, and distribute as long as 
 * as credit is given to the original creator me, Philippe Ajoux.
 * If you have any comments, suggests, requests, or found a bug
 * please send me an e-mail and let me know. You will help everyone.
 * 
 * -----[2] 05.01.07 --------------
 * Update/Revised class to work for [Revision 62] of Papervision3D
 * Completely changed the way vertices/faces are updated/animated
 * Less memory is used to do it
 * More efficient
 * Animation is moved from render() to project()
 * 
 * -----[1] 01.09.07 --------------
 * Original release that worked with original [Revision ?] of Papervision3D
 * (Revision # unknown, but it was first "closed" beta release)
 * 
 * @version 05.01.07
 * @author Philippe Ajoux (philippe.ajoux@gmail.com)
 */
package d3s.net.papervision3d.objects
{
	import d3s.net.papervision3d.animation.Frame;
	
	import flash.display.Sprite;
	import flash.utils.getTimer;
	
	import org.papervision3d.core.geom.Face3D;
	import org.papervision3d.core.geom.Mesh3D;
	import org.papervision3d.core.geom.Vertex2D;
	import org.papervision3d.core.geom.Vertex3D;
	import org.papervision3d.core.proto.MaterialObject3D;
	import org.papervision3d.core.proto.SceneObject3D;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.core.proto.CameraObject3D;
	import org.papervision3d.core.proto.TraverseCallback;
	import flash.events.Event;
	
	/**
	 * KeyframeMesh provides framework for objects that have keyframed animation.
	 * Note that is class is [abstract] in that in itself provides no functionality.
	 * <p>
	 * There are a couple very specific details that must be adhered to by all subclasses
	 * in order for this to work properly:
	 * <p>
	 * [1] The subclass MUST allocate properly sized arrays with memory for <i>faces</i> and <i>vertices</i><br>
	 * [2] The <i>Face3D</i> objects in <i>faces</i> must have an <i>id</i> cooresponding to their original array order
	 * <p>
	 * Please feel free to use, but please mention me!
	 * 
	 * @version 05.01.07
	 * @author Philippe Ajoux (philippe.ajoux@gmail.com)
	 */
	public class KeyframeMesh extends Mesh3D
	{
		/**
		 * Three kinds of animation sequences:
		 *  [1] Normal (sequential, just playing)
		 *  [2] Loop   (a loop)
		 *  [3] Stop   (stopped, not animating)
		 */
		public static const ANIM_NORMAL:int = 1;
		public static const ANIM_LOOP:int = 2;
		public static const ANIM_STOP:int = 4;
		public static const ANIM_BWD:int = 8;
		
		/**
		 * The array of frames that make up the animation sequence.
		 */
		public var frames:Array = new Array();
		
		/**
		 * Keep track of the current frame number and animation
		 */
		private var _currentFrame:int = 0;
		private var interp:Number = 0;
		private var start:int, end:int, type:int;
		private var ctime:Number = 0, otime:Number = 0;
		
		/**
		 * Number of animation frames to display per second
		 */
		public var fps:int;
		
		/**
		 * KeyframeMesh is a class used [internal] to provide a "keyframe animation"/"vertex animation"/"mesh deformation"
		 * framework for subclass loaders. There are some subtleties to using this class, so please, I suggest you
		 * don't (not yet). Possible file formats are MD2, MD3, 3DS, etc...
		 */
		public function KeyframeMesh(material:MaterialObject3D, fps:int = 6, scale:Number = 1, initObject:Object = null)
		{
			super(material, null, initObject);
			this.fps = fps;
			trace( "KeyframeMesh -- fps : ", fps );
			scale = scale;
		}
		
		public function gotoAndPlay(frame:int):void
		{
			keyframe = frame;
			type = ANIM_NORMAL;
		}
		
		public function loop(start:int, end:int):void
		{
			this.start = (start % frames.length);
			keyframe = start;
			this.end = (end % frames.length);
			type = ANIM_LOOP;
		}
		
		public function stop():void
		{
			type = ANIM_STOP;
		}
		
		public function play() : void {
			type = ANIM_NORMAL;
		}
		public function playBackward() : void {
			type = ANIM_BWD;
		}
		
		
		public function gotoAndStop(frame:int):void
		{
			keyframe = frame;
			type = ANIM_STOP;
		}
		
		// MESSY STILL MESSY STILL MESSY
		public override function project(parent:DisplayObject3D, camera:CameraObject3D, callback:TraverseCallback):void
		{
			ctime = getTimer();
			
			var way : int = ( type == ANIM_BWD ) ? -1 : 1;
			var dst:Vertex3D, a:Vertex3D, b:Vertex3D;
			var vertices:Array = this.vertices;
			var cframe:Frame, nframe:Frame;
			var i:int;
			
			if( _currentFrame == frames.length - 1 ) dispatchEvent( new Event( Event.COMPLETE ) );
			
			cframe = frames[_currentFrame];
			var nf : int = (_currentFrame + way);
			if( nf < 0 ) nf += frames.length;
			nframe = frames[ nf % frames.length];
			
			for (i = 0; i < vertices.length; i++)
			{
				dst = vertices[i];
				a = cframe.vertices[i];
				b = nframe.vertices[i];
				dst.x = a.x + interp * (b.x - a.x);
				dst.y = a.y + interp * (b.y - a.y);
				dst.z = a.z + interp * (b.z - a.z); 
			}

			// Update the timer part, to get time based animation
			
			if (type != ANIM_STOP)
			{
				interp += fps * (ctime - otime) / 1000;
				if (interp >= 1)
				{
					if (type == ANIM_LOOP && _currentFrame + 1 == end){
						dispatchEvent( new Event( Event.COMPLETE ) );
						keyframe = start;
					}else
						keyframe += way;
					interp = 0;
				}
			}
			otime = ctime;
			
			return super.project(parent, camera, callback);
		}
		
		public function get keyframe():int { return _currentFrame; }
		public function set keyframe(i:int):void {
			if( i < 0 ) i += frames.length;
			_currentFrame = i % frames.length; 
		}
	}
}